/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package parser

import scala.util.parsing.input.Position
import Messages._

object Results {

  case class ~[+A, +B](_1: A, _2: B) {
    override def toString() = "%s ~ %s".format(_1, _2)
  }

  /**
   * The result of analysis
   *
   * @param msgs error or warning messages.
   */
  abstract class Result[+T](val msgs: List[Message]) { 
    /**
     * Analysis is successful?
     */
    def successful(): Boolean
    
    /**
     * Concats another result.
     */
    def ~[U](rhs: Result[U]): Result[~[T, U]]

    /**
     * Converts a content.(converting may fail.)
     */
    def flatMap[U](f: T => Result[U]): Result[U]

    /**
     * Converts a content.
     */
    def map[U](f: T => U): Result[U]

    def mapConst[U](f: => U): Result[U] = map { _ => f }

    def filter(p: T => Boolean): Result[T]

    def withFilter(p: T => Boolean): WithFilter = new WithFilter(this, p)

    def foreach(f: T => Unit): Unit

    class WithFilter(self: Result[T], p: T => Boolean) {
      def map[U](f: T => U): Result[U] = self.filter(p).map(f)
      def flatMap[U](f: T => Result[U]): Result[U] = self.filter(p).flatMap(f)
      def foreach(f: T => Unit): Unit = self.filter(p).foreach(f)
      def withFilter(q: T => Boolean): WithFilter = new WithFilter(self, x => p(x) && q(x))
    }
  }

  /**
   * The result of analysis when analysis is successful.
   *
   * @param node Syntax tree generated by analysis.
   */
  class Success[+T](val node: T, msgs: List[Message]) extends Result[T](msgs) {
    override def successful(): Boolean = true
    override def ~[U](rhs: Result[U]): Result[~[T, U]] = rhs match {
      case Success(rnode, rmsgs) => Success(new ~(node, rnode), msgs ++ rmsgs)
      case Failure(rmsgs)        => Failure(msgs ++ rmsgs)
    }

    override def flatMap[U](f: T => Result[U]): Result[U] = {
      f(node) match {
        case Success(n, m) => Success(n, msgs ++ m)
        case Failure(m) => Failure(msgs ++ m)
      }
    }
    override def map[U](f: T => U): Result[U] = flatMap( { x => Success(f(x)) } )

    override def foreach(f: T => Unit): Unit = { f(node) }

    override def filter(p: (T) => Boolean): Result[T] = if (p(node)) this else Failure(Nil)
  }

  /**
   * The result of analysis when analysis fails.
   */
  class Failure(msgs: List[Message]) extends Result[Nothing](msgs) {
    override def successful(): Boolean = false
    override def ~[U](rhs: Result[U]): Result[~[Nothing, U]] = Failure(msgs ++ rhs.msgs)
    override def flatMap[U](f: Nothing => Result[U]): Result[U] = this
    override def map[U](f: Nothing => U): Result[U] = this
    override def filter(p: Nothing => Boolean): Result[Nothing] = this
    override def foreach(f: Nothing => Unit): Unit = Unit
  }

  object Success {
    def apply[T](node: T) = new Success(node, Messages.emptyMessages)
    def apply[T](node: T, msgs: List[Message]) = new Success(node, msgs)
    def unapply[T](s: Success[T]): Option[(T, List[Message])] = Some((s.node, s.msgs))
  }

  object Failure {
    def apply(msgs: List[Message]) = new Failure(msgs)
    def unapply(s: Failure): Option[(List[Message])] = Some(s.msgs)
  }

  /**
   * Makes tuple analysis result from two analysis results.
   */
  def mkTuple[T, U](t: Result[T], u: Result[U]): Result[(T, U)] = (t ~ u).map { case t ~ u => (t, u) }

  /**
   * Analyzes value wrapped by option using f.
   */
  def processOpt[T, U](opt: Option[T])(f: T => Result[U]): Result[Option[U]] = {
    opt.map(f) match {
      case Some(x) => x.map(Some(_))
      case None => Success(None)
    }
  }

  /**
   * Convers list of analysis results to analyzes result which contain list.
   */
  def mergeList[T](ss: List[Result[T]]): Result[List[T]] = {
    val s0: Result[List[T]] = Success(List[T]())
    ss.foldRight(s0) { (l, r) => (l ~ r).map { case x ~ xs => x::xs  } }
  }

  /**
   * Creates Failure with a error message.
   */
  def failure(p: Position, fmt: String, args :Any*) = Failure(List(Messages.error(p, fmt, args:_*)))

/*
  /**
   * If cond is true, return faiure with a error Messages
   */
  def ifFailure(cond: Boolean, pos: Position, fmt: String, args: Any*) = if (cond) Success(Unit) else failure(pos, fmt, args:_*)
*/

  /**
   * Creates Result from Option[String].
   *
   * If None, return Success, otherwise Failure with the error message.
   */
  def fromOption(pos: Position, errorMessage: Option[String]): Result[Unit] = errorMessage match {
    case None      => Success(Unit)
    case Some(msg) => failure(pos, msg)
  }

  /**
   * Creates Result from Either[T, String].
   *
   * If Left, return Success, otherwise Failure.
   */
  def fromEither[T](pos: Position, either: Either[T, String]): Result[T] = either match {
    case Left(value) => Success(value)
    case Right(msg)  => failure(pos, msg)
  }
}
